package com.example.encrypt_demo.util;

import org.apache.commons.codec.binary.Hex;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.stream.Stream;

import static org.apache.catalina.manager.Constants.CHARSET;

/**
 * @author benjamin_5
 * @Description
 * @date 2024/9/6
 */
public class RsaUtil {
    /**
     * RSA
     */
    private static final String RSA_ALG = "RSA";
    /**
     * RSA签名填充方式
     */
    private static final String RSA_PSS_SIGN_ALGORITHM = "SHA256WithRSA/PSS";

    private static final int MAX_ENCRYPT_BLOCK = 62;

    private static final int MAX_DECRYPT_BLOCK = 128;

    public static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJnU0WPhSeS6BhKqM9YWzFGnQkw1j/8jUzWZ/vNhxgu7obcghf8ZS2E2h4B33HbQxuOE4xU5/SzJOdFZTU4z6Mtzdg33Fa+BbeVGWGbzw8KReK1mzqolcIzcQVBqTD6CO4JZp8cR80neCQNSi+FeQnFFupvgQHaRxSH62Kd8AaXBAgMBAAECgYB46nm0Ys+shDRl03dvXvg3bpuEP83yJJgxV9y4/i+n5VKWGXB6Ni20OiKEbrLSigTrrfa5Jp3E6F8UGXfUcqUWmwNNJqxKmuJxbNBez4xiFLG3Uo1wNlMrpy7lC3cOY3hnj23FYFiiJ75w2GTzOATbDdZ3eZPh7eGnHTxLrdQ04QJBAOdU6atcaLnEzRkZCThwPtaLfiGUpE/wjm4oB+phPp1TmrrT/QN0sg+0bq7GobhCQ1plS/9+wxuTPwo/qJqkgGUCQQCqPDwcIN/vTVsCtZzihRelzA7HQWJCIwYuRwWExxSiY8y+/k31BUtc5y26CPSQZxzcRJ4q57XbscmAW+LFp4QtAkATSze5/RkW9V0NzNLCDEqRZkz8zALwT+XxNhGC8N1iDckH8ApYMXFwceIRXau7THuFnJE8G/x13lARsMGFNFcpAkEAifH5e4mgLJfwccec82mJ+g3aw9fVuWYMMh/W9vsjW1uno/UKUswRf5zNTvzGwOTtXAg2auoMUZRFhj/vCN9vmQJAJOujl5vLeeOBDYKtjP2Kz/gvKqiEocQWlenivUtIhbiYxarnvR1QpcnQ1+V1t8H5+d+Tuv4XPN05skdR+qw+IA==";
    public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCZ1NFj4UnkugYSqjPWFsxRp0JMNY//I1M1mf7zYcYLu6G3IIX/GUthNoeAd9x20MbjhOMVOf0syTnRWU1OM+jLc3YN9xWvgW3lRlhm88PCkXitZs6qJXCM3EFQakw+gjuCWafHEfNJ3gkDUovhXkJxRbqb4EB2kcUh+tinfAGlwQIDAQAB";

    public static final String SIGN_PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKpKgupEORx1mn5RPdNeSUlfNLOU/C9E/0IjHbzk4rKg4kvK7Y8ONxKubUor89vWdBGFkeGdBqoMns2072d747kMBfyEubRKGO6OR/8lrKmlZ9gI2AOod5VwNI2A6Wy7BemzRd2ayQSe7A4UhYrOSkurv0bQY363RX1NHbQ7Fq8tAgMBAAECgYAk9acUUVpT4vJ1GAzjI0OkuRUnJN7XI5KDODvsE4FgY6q56IFG2z74vwLZZlC3FgR7CRJwdTRNFBAjdXHAdBMoa+CPFNiACih7ibipM4QEFiPG85YdYEBOdvINWZNqyjvdG8n/oNT9WSx3bUzmSAdOsCutUXd+3UwXXFvv40JnwQJBANKwdQuBw/u+T8QgqtXUyT4dGj7n80um/npKUXPxJCshvldQ94vRO25+RD2DdjrmwGo/SgMUoepE9YwL8QTZ1xMCQQDO6evgLFULrlC2NWMRHgDCuRV8PYJIH4PTD+FrsjrINmUaLEUt/CNa3vKOE0szQJRWC/uzl7YgmjgfwAxegui/AkAErKGMJc7hUPGYGllv8Q6d8Cta5e6B6SyQLgHsnpb81fzRFxzG31eANhDxhG8lE2WFde4Ab37AIdOIzeMFBog9AkAqF4QYcmxKmAdrSakBjmzxL92jDJA4Q9D5Wr2svvEeUZJ3pJHeLPC0R65oIJynrHeAmad2fWJbd4P7UYIyp8bZAkB0KiXIXa8morjqQ5ZnqKlQ9aLWYxg7ToZKHp+Bd+1c4npLb+N5L3WOhzkIO2/EPwXE9pu2VWA6ULhetZ3wNMsv";
    public static final String SIGN_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqSoLqRDkcdZp+UT3TXklJXzSzlPwvRP9CIx285OKyoOJLyu2PDjcSrm1KK/Pb1nQRhZHhnQaqDJ7NtO9ne+O5DAX8hLm0Shjujkf/JayppWfYCNgDqHeVcDSNgOlsuwXps0XdmskEnuwOFIWKzkpLq79G0GN+t0V9TR20OxavLQIDAQAB";

    /**
     * 获取公钥
     *
     * @param publicKeyStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey getPublicKey(String publicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] decode = Base64.getDecoder().decode(publicKeyStr);
        PublicKey publicKey = KeyFactory.getInstance(RSA_ALG).generatePublic(new X509EncodedKeySpec(decode));
        return publicKey;
    }

    /**
     * 公钥加密
     * @param data
     * @param publicKeyStr
     * @return
     * @throws Exception
     */
    public static String rsaEncrypt(String data, String publicKeyStr) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {

            byte[] cache;
            PublicKey publicKey = getPublicKey(publicKeyStr);
            Cipher cipher = Cipher.getInstance(RSA_ALG);
            byte[] dataBytes = data.getBytes(CHARSET);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int dataLen = dataBytes.length;
            int afterLocal = 0;
            while (dataLen - afterLocal > 0) {
                if (dataLen - afterLocal > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(dataBytes, afterLocal, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(dataBytes, afterLocal, dataLen - afterLocal);
                }
                out.write(cache, 0, cache.length);
                afterLocal = afterLocal + MAX_ENCRYPT_BLOCK;
            }
            byte[] rsaEncrypt = out.toByteArray();
            return Base64.getEncoder().encodeToString(rsaEncrypt);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("RSA 加密失败");
        }finally {
            out.close();
        }
    }

    /**
     * 获取私钥
     *
     * @param privateKeyStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey getPrivateKey(String privateKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] decode = Base64.getDecoder().decode(privateKeyStr);
        PrivateKey privateKey = KeyFactory.getInstance(RSA_ALG).generatePrivate(new PKCS8EncodedKeySpec(decode));
        return privateKey;
    }

    /**
     * 私钥解密
     * @param data
     * @param privateKeyStr
     * @return
     * @throws Exception
     */
    public static String rsaDecrypt(String data, String privateKeyStr) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            byte[] cache;
            byte[] decode = Base64.getDecoder().decode(data.getBytes(CHARSET));
            PrivateKey privateKey = getPrivateKey(privateKeyStr);
            Cipher cipher = Cipher.getInstance(RSA_ALG);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            int dataLen = decode.length;
            int afterLocal = 0;
            while (dataLen - afterLocal > 0) {
                if (dataLen - afterLocal > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(decode, afterLocal, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(decode, afterLocal, dataLen - afterLocal);
                }
                out.write(cache, 0, cache.length);
                afterLocal = afterLocal + MAX_DECRYPT_BLOCK;
            }
            return out.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("RSA 解密失败");
        } finally {
            out.close();
        }
    }

    /**
     * 验签
     * @param params
     * @param publicKey
     * @param sign
     * @return
     * @throws RuntimeException
     */
    public static boolean verify(Map<String, String[]> params, String publicKey, String sign) throws RuntimeException {
        try {
            String paramStr = getSortParam(params);
            PublicKey pubKey = getPublicKey(publicKey);
            Signature signature = Signature.getInstance(RSA_PSS_SIGN_ALGORITHM);
            signature.initVerify(pubKey);
            signature.update(paramStr.getBytes(CHARSET));
            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (Exception e) {
            throw new RuntimeException("RSA验证签名失败", e);
        }
    }

    /**
     * 生成签名
     * @param params
     * @param
     * @return
     * @throws RuntimeException
     */
    public static String sign(Map<String, String[]> params, String privateKey) throws RuntimeException {
        if (null == params) {
            throw new RuntimeException("RSA签名参数为空");
        }
        try {
            String paramStr = getSortParam(params);
            PrivateKey key = getPrivateKey(privateKey);
            Signature signature = Signature.getInstance(RSA_PSS_SIGN_ALGORITHM);
            signature.initSign(key);
            signature.update(paramStr.getBytes(CHARSET));
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            throw new RuntimeException("RSA生成签名失败", e);
        }
    }

    /**
     * 参数排序
     * @param param
     * @return
     */
    public static String getSortParam(Map<String, String[]> param) {
        Set<String> keys = param.keySet();
        String[] paramArr = new ArrayList<>(keys).toArray(new String[keys.size()]);
        // 按字典序排序
        Arrays.sort(paramArr);
        StringBuilder paramStr = new StringBuilder();
        Stream.of(paramArr).forEach(key -> {
            String[] values = param.get(key);
            if(values != null && values.length > 0){
                paramStr.append(key).append("=").append(values[0]).append("&");
            }else{
                paramStr.append(key).append("=null&");
            }
        });
        return paramStr.substring(0, paramStr.length() - 1);
    }



}
